<template>
  <div
    :id="pid"
    class="video-to-map"
    :class="{
      bigScreen: isZoomIn,
      drawing: drawing,
      end: drawItem?.finish?.length,
      electronicFence: electronicFenceDrawList.length,
    }"
    oncontextmenu="return false"
    ondragstart="return false"
  >
    <!-- 预置位 -->
    <div
      class="preset"
      :class="{ on: presetShow }"
      @click="doPresetShow"
      @dblclick.stop
    >
      <span>预置位</span>
      <i class="iconfont icon-shijianxiangqingzhankai jt" />
      <div class="preset-list" @click.stop>
        <div
          class="item"
          :class="{ on: choosedPreset === +item.presetId }"
          v-for="(item, index) in presetList"
          :key="index"
          @click.stop="choosePreset(item)"
        >
          <span
            :c-tip="item.presetName"
            c-tip-class="c-tip-normal"
            c-tip-auto
            >{{ item.presetName }}</span
          >
          <i
            class="iconfont icon-bianjiyuzhiwei"
            c-tip="转动至预置位"
            c-tip-class="c-tip-normal"
            @click.stop="gotoPreset(item)"
          />
        </div>
        <span v-if="!presetList.length" class="c-no-datas-block">暂无数据</span>
      </div>
    </div>
    <!-- 功能按钮 -->
    <div class="v-btns" @click.stop @dblclick.stop>
      <div class="btn">
        <el-switch
          v-model="electronicFenceSwitch"
          active-color="#1172E5"
          @change="getElectronicFence()"
          c-tip="电子围栏"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="doDrawing(1)">
        <i
          class="iconfont"
          :class="
            drawing === 1
              ? 'icon-shipinzujian_icon_shipinzhaodi_s'
              : 'icon-shipinzujian_icon_shipinzhaodi_n'
          "
          c-tip="以视频找地"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="cloudControlShow()">
        <i
          class="iconfont"
          :class="
            cloudControlSwitch
              ? 'icon-shipinzujian_icon_yuntaifangxiang_s'
              : 'icon-shipinzujian_icon_yuntaifangxiang_n'
          "
          c-tip="云台操控"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="doPtzShow(1)">
        <i
          class="iconfont"
          :class="
            ptzShow === 1
              ? 'icon-shipinzujian_icon_PTZ_s'
              : 'icon-shipinzujian_icon_PTZ_n'
          "
          c-tip="转动到PTZ"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
        <div v-if="ptzShow === 1" class="goto-ptz" @click.stop>
          <input type="text" v-model="ptz.pan" placeholder="P" />
          <input type="text" v-model="ptz.tilt" placeholder="T" />
          <input type="text" v-model="ptz.zoom" placeholder="Z" />
          <span @click="gotoPtz()">转动</span>
        </div>
      </div>
      <div class="btn" @click="doPtzShow(2)">
        <i
          class="iconfont"
          :class="
            ptzShow === 2
              ? 'icon-shipinzujian_icon_huoquPTZ_s'
              : 'icon-shipinzujian_icon_huoquPTZ_n'
          "
          c-tip="PTZ信息"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
        <div v-if="ptzShow === 2" class="ptz-info" @click.stop>
          <span>P:{{ resultPtz.pan }}</span>
          <span>T:{{ resultPtz.tilt }}</span>
          <span>Z:{{ resultPtz.zoom }}</span>
        </div>
      </div>
      <div class="btn" @click="doDrawing(2)">
        <i
          class="iconfont"
          :class="
            drawing === 2
              ? 'icon-shipinzujian_icon_ceju_s'
              : 'icon-shipinzujian_icon_ceju_n'
          "
          c-tip="测距"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="doDrawing(3)">
        <i
          class="iconfont"
          :class="
            drawing === 3
              ? 'icon-shipinzujian_cemian_s'
              : 'icon-shipinzujian_cemian_n'
          "
          c-tip="测面积"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="doDrawing(4)">
        <i
          class="iconfont"
          :class="
            drawing === 4
              ? 'icon-shipinzujian_icon_cegaodu_s'
              : 'icon-shipinzujian_icon_cegaodu_n'
          "
          c-tip="测高度"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
      <div class="btn" @click="clear()">
        <i
          class="iconfont icon-shipinzujian_qingchu_n"
          c-tip="清除"
          c-tip-placement="bottom"
          c-tip-class="c-tip-normal"
        />
      </div>
    </div>
    <!-- 画板 -->
    <div
      class="draw-canvas"
      @mousedown.stop
      @click.stop
      @dblclick="stopDblclick"
    >
      <canvas
        :id="'draw_canvas_' + pid"
        :width="size.w"
        :height="size.h"
      ></canvas>
    </div>
    <div
      v-if="(drawing === 1 || mapDrawing === 1) && drawItem?.finish?.length"
      class="find-video-info"
    >
      <div class="item">
        <span class="ll">地块周长：</span>
        <span class="num">{{ drawItem.distanceSum || "-" }}m</span>
      </div>
      <div class="item">
        <span class="ll">地块面积：</span>
        <span class="num">{{ drawItem.area || "-" }}m²</span>
      </div>
      <div class="item">
        <span class="ll">到摄像机距离：</span>
        <span class="num">{{ drawItem.toDeviceDistance || "-" }}m</span>
      </div>
    </div>
    <div
      v-if="drawing === 3 && drawItem?.finish?.length"
      class="draw-area"
      :style="{
        top: drawItem.points[0].percentY * 100 + '%',
        left: drawItem.points[0].percentX * 100 + '%',
      }"
    >
      <span>总面积：{{ drawItem.area || "-" }}平方米</span>
      <i
        class="iconfont icon-tongyong_icon_lishijilushanchu"
        @click="clear()"
      />
    </div>
    <template
      v-if="
        drawing === 2 && drawItem?.finish?.length && drawItem?.distances?.length
      "
    >
      <div
        class="draw-distance"
        v-for="(item, index) in drawItem.points"
        :key="index"
        :style="{
          top: item.percentY * 100 + '%',
          left: item.percentX * 100 + '%',
        }"
      >
        <span>{{ fitDistance(index) }}</span>
        <i
          v-if="index !== 0 && drawItem.points.length > 2"
          class="iconfont icon-tongyong_icon_qingkong"
          @click="removePoint(index)"
        />
        <i
          v-if="index === drawItem.points.length - 1"
          class="iconfont icon-tongyong_icon_lishijilushanchu"
          @click="clear()"
        />
      </div>
    </template>
    <template v-if="drawing === 4 && drawItem?.heights?.length">
      <div
        class="draw-distance"
        v-if="fitHeight(index) || drawItem?.finish?.length"
        v-for="(item, index) in drawItem.heights"
        :key="index"
        :style="{
          top: item.percentY * 100 + '%',
          left: item.percentX * 100 + '%',
        }"
      >
        <span v-if="fitHeight(index)">{{ fitHeight(index) }}</span>
        <i
          v-if="
            drawItem?.finish?.length &&
            index !== 0 &&
            drawItem.heights.length > 2
          "
          class="iconfont icon-tongyong_icon_qingkong"
          @click="removePoint(index, 2)"
        />
        <i
          v-if="
            drawItem?.finish?.length && index === drawItem.heights.length - 1
          "
          class="iconfont icon-tongyong_icon_lishijilushanchu"
          @click="clear(false)"
        />
      </div>
    </template>
    <template v-for="item in electronicFenceDrawList">
      <div
        v-if="item.data.featureId"
        class="electronicFence-selected"
        :class="{ before: item.data.selected }"
        :style="{
          left: item.data.center.percentX * 100 + '%',
          bottom: 100 - item.data.center.percentY * 100 + '%',
        }"
      >
        <div v-if="item.data.selected" class="info">
          <div class="til">{{ choosedLayer.data.label }}</div>
          <div class="con" v-if="choosedFreckle.data">
            <!-- 土地利用现状 -->
            <template v-if="choosedLayer.data.abbreviation === 'xztb'">
              <span class="item"
                >图斑编号：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
              <span class="item"
                >地类名称：<span :c-tip="choosedFreckle.data.dlmc" c-tip-auto>{{
                  choosedFreckle.data.dlmc
                }}</span></span
              >
              <span class="item"
                >坡度级别：<span
                  :c-tip="choosedFreckle.data.pdjbF"
                  c-tip-auto
                  >{{ choosedFreckle.data.pdjbF }}</span
                ></span
              >
            </template>
            <!-- 土地利用总体规划 -->
            <template v-else-if="choosedLayer.data.abbreviation === 'jsydgzq'">
              <span class="item"
                >标识码：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
              <span class="item"
                >管制区类型：<span
                  :c-tip="choosedFreckle.data.gzqlxdm"
                  c-tip-auto
                  >{{ choosedFreckle.data.gzqlxdm }}</span
                ></span
              >
            </template>
            <!-- 基本农田 -->
            <template v-else-if="choosedLayer.data.abbreviation === 'yjjbnt'">
              <span class="item"
                >图斑编号：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
              <span class="item"
                >地类名称：<span :c-tip="choosedFreckle.data.dlmc" c-tip-auto>{{
                  choosedFreckle.data.dlmc
                }}</span></span
              >
              <span class="item"
                >图斑面积：<span
                  :c-tip="choosedFreckle.data.yjjbnttbmj"
                  c-tip-auto
                  >{{ choosedFreckle.data.yjjbnttbmj }}平方米</span
                ></span
              >
              <span class="item"
                >坡度级别：<span
                  :c-tip="choosedFreckle.data.pdjbF"
                  c-tip-auto
                  >{{ choosedFreckle.data.pdjbF }}</span
                ></span
              >
            </template>
            <!-- 生态红线 -->
            <template v-else-if="choosedLayer.data.abbreviation === 'stbhhx'">
              <span class="item"
                >红线编码：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
              <span class="item"
                >红线名称：<span :c-tip="choosedFreckle.data.hxmc" c-tip-auto>{{
                  choosedFreckle.data.hxmc
                }}</span></span
              >
              <span class="item"
                >红线类型：<span :c-tip="choosedFreckle.data.hxlx" c-tip-auto>{{
                  choosedFreckle.data.hxlx
                }}</span></span
              >
              <span class="item"
                >自然保护地级别：<span
                  :c-tip="choosedFreckle.data.zrbhdjb"
                  c-tip-auto
                  >{{ choosedFreckle.data.zrbhdjb }}</span
                ></span
              >
            </template>
            <!-- 土地报批 -->
            <template v-else-if="choosedLayer.data.abbreviation === 'tdbp'">
              <span class="item"
                >地块编号：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
              <span class="item"
                >项目名称：<span :c-tip="choosedFreckle.data.xmmc" c-tip-auto>{{
                  choosedFreckle.data.xmmc
                }}</span></span
              >
              <span class="item"
                >批复文号：<span :c-tip="choosedFreckle.data.pzwh" c-tip-auto>{{
                  choosedFreckle.data.pzwh
                }}</span></span
              >
            </template>
            <!-- 其他类型 -->
            <template v-else>
              <span class="item"
                >标识码：<span :c-tip="item.data.tbbh" c-tip-auto>{{
                  item.data.tbbh
                }}</span></span
              >
            </template>
          </div>
        </div>
        <div
          class="feature"
          @click="chooseElectronicFence({ target: item })"
          :c-tip="item.data.tbbh"
          c-tip-auto
        >
          {{ item.data.tbbh }}
        </div>
      </div>
    </template>
    <Ts ref="ts" class="ts" />
    <!-- 功能按钮 -->
    <div v-if="main === 2" class="r-btns" @dblclick.stop>
      <div
        class="btn"
        @click="doZoom()"
        :c-tip="!isZoomIn ? '放大' : '缩小'"
        c-tip-placement="right"
        c-tip-class="c-tip-normal"
      >
        <i
          class="iconfont"
          :class="!isZoomIn ? 'icon-fangda2' : 'icon-AR-suoxiao'"
        />
      </div>
      <div
        class="btn"
        @click="doOut()"
        c-tip="退出"
        c-tip-placement="right"
        c-tip-class="c-tip-normal"
      >
        <i class="iconfont icon-tuichuhuifang item"></i>
      </div>
    </div>
  </div>
</template>
<script>
import { fabric } from "fabric";
import {
  doControlPreset,
  queryPresetList,
  queryPolygonPatchShow,
  pointsValid,
  turnLatitudeByPoint,
  doPtzLocationCmd,
  getXjxlFeaturesByIds,
  getLayerTypeAttrFilter,
  queryPtzPositionNew,
  getCameraCoord,
} from "@/api/video/video";
import Ts from "@/components/canvas/ts.vue";
import { getLineStringLength, getPolygonArea } from "@/utils/openLayerCommon";
import { queryByLayerIdAndFeatureId } from "@/api/spotPlotting";
import { queryPtzPosition } from "@/api/video/videoPTZ";
const TOOL_BTNS_WIDTH = 46; //快捷功能按钮宽度
export default {
  components: {
    Ts,
  },
  props: {
    deviceCode: {
      //设备编码
      type: String,
    },
    channelCode: {
      //通道编码
      type: String,
    },
    myTransform: {
      //缩放比例
      type: Number,
      default: 1,
    },
    vueIns: {
      //分屏传入this
      type: Object,
      default: () => {
        return {};
      },
    },
    division: {
      //判断分屏视频定位是否启用
      type: String,
      default: "0",
    },
    cloudControlSwitch: {
      //云台操控
      type: Boolean,
      default: true,
    },
    unCenter: {
      //视图联动弹出窗口是否居中,false默认居中
      type: Boolean,
      default: false,
    },
    lng: {}, //摄像机坐标
    lat: {}, //摄像机坐标
    main: {}, //视图联动主屏, 1: 视频, 2: 地图
    isZoomIn: {}, //视图联动辅窗放大缩小
  },
  data() {
    return {
      pid: new Date().getTime(), //页面唯一id
      minWidth: 960, //最小宽度
      minHeight: 540, //最小高度
      presetShow: false, //预置位显示隐藏
      presetList: [], //预置位列表
      choosedPreset: false, //选中的预置位id
      choosedLayer: {
        //选中的图层
        id: "", // id
        data: "", //图层数据
      },
      choosedFreckle: {
        //选中的图斑
        id: "", // id
        data: "", //图斑数据
      },
      electronicFenceSwitch: true, //电子围栏开关
      electronicFenceDrawList: [], //电子围栏标绘列表
      drawCanvas: false, //画板
      drawItem: false, //以视频找地、测距、测面积标绘
      size: {
        //画板大小
        w: 0,
        h: 0,
      },
      drawing: false, //标绘中, 1: 以视频找地, 2: 测距, 3: 测面积, 4: 测高度
      mapDrawing: false, //地图标绘标识
      sort: {
        //排序
        key: "presetName", //关键字
        asc: true, //是否升序
      },
      ptzShow: false, //1: 转动到PTZ弹窗, 2: ptz信息弹窗
      ptz: {
        //转动到ptz参数
        pan: "",
        tilt: "",
        zoom: "",
      },
      resultPtz: {
        //ptz信息参数
        pan: "",
        tilt: "",
        zoom: "",
      },
    };
  },
  watch: {
    /**
     * ptz数字录入
     */
    ptzInput(a, b) {
      Object.keys(a).forEach((key) => {
        const it = a[key];
        if (!it) {
          return false;
        }
        if (isNaN(Number(it)) && (key !== "tilt" || it !== "-")) {
          this.ptz[key] = b[key];
        } else {
          this.ptz[key] = it.replace(/[. ]/g, "");
        }
        if (key === "pan" && (it < 0 || it > 360)) {
          this.ptz[key] = b[key];
          $message.w("P值支持输入0~360");
        }
        if (key === "tilt" && (it < -90 || it > 90)) {
          this.ptz[key] = b[key];
          $message.w("T值支持输入-90~90");
        }
        if (key === "zoom" && (it < 1 || it > 50)) {
          this.ptz[key] = b[key];
          $message.w("Z值支持输入1~50");
        }
      });
    },
  },
  computed: {
    /**
     * ptz数字录入
     */
    ptzInput() {
      return JSON.parse(JSON.stringify(this.ptz));
    },
  },
  created() {
    if (Object.keys(this.vueIns).length) {
      this.$store = this.vueIns.$store;
    }
    this.pid = this.getUUID();
    this.$emit("update:main", 1);
    this.$emit("update:isZoomIn", false);
  },
  mounted() {
    console.log("=============视图联动开启=============");
    this.isTenant =
      Object.keys(this.vueIns).length === 0
        ? this.$store.getters.userInfo.tenantId
        : this.vueIns.$store.getters.userInfo.tenantId;
    this.userId =
      Object.keys(this.vueIns).length === 0
        ? this.$store.getters.userInfo.userId
        : this.vueIns.$store.getters.userInfo.userId;
    this.permissions =
      Object.keys(this.vueIns).length === 0
        ? this.$store.getters.permissions
        : this.vueIns.$store.getters.permissions;
    this.initViewBox();
    this.initCanvas();
    this.resizeObserver = new ResizeObserver(() => {
      this.limitMinSize();
      clearTimeout(this.resizeTiming);
      this.resizeTiming = setTimeout(() => {
        this.initCanvas();
        this.fitFeatureDialog();
      }, 200);
    });
    this.resizeObserver.observe($("#" + this.pid)[0]);
    this.getPresetList();
    this.cloudControlShow(true);
  },
  methods: {
    /**
     * 转动到ptz
     */
    gotoPtz() {
      if (!this.ptz.pan || this.ptz.pan < 0 || this.ptz.pan > 360) {
        $message.w("P值输入不合法!");
        return false;
      }
      if (!this.ptz.tilt || this.ptz.tilt < -90 || this.ptz.tilt > 90) {
        $message.w("T值输入不合法!");
        return false;
      }
      if (!this.ptz.zoom || this.ptz.zoom < 1 || this.ptz.zoom > 50) {
        $message.w("Z值输入不合法!");
        return false;
      }
      this.doGotoPtz(this.ptz, true);
    },
    /**
     * 1: 打开转动到ptz弹框, 2: 打开ptz信息弹框
     */
    doPtzShow(type) {
      Object.assign(this.ptz, this.$options.data().ptz);
      Object.assign(this.resultPtz, this.$options.data().resultPtz);
      if (this.ptzShow === type) {
        type = false;
      }
      this.ptzShow = type;
      if (type === 2) {
        this.getPtz();
      }
    },
    /**
     * 查询ptz信息
     */
    async getPtz() {
      clearTimeout(this.getPtzTiming);
      if (this.ptzShow !== 2) {
        return false;
      }
      const params = {
        deviceCode: this.deviceCode,
        channelCode: this.channelCode,
      };
      let resp;
      if (this.isTenant) {
        resp = await queryPtzPosition(params);
      } else {
        resp = await queryPtzPositionNew(params);
      }
      if (this.ptzShow !== 2) {
        return false;
      }
      if (resp.code === 200) {
        this.resultPtz.pan = resp.data.pan;
        this.resultPtz.tilt = resp.data.tilt;
        this.resultPtz.zoom = resp.data.zoom;
      } else {
        $message.a(resp);
      }
      this.getPtzTiming = setTimeout(() => {
        this.getPtz();
      }, 3000);
    },
    /**
     * 阻止双击
     */
    stopDblclick(e) {
      if (this.drawing) {
        e.stopPropagation();
      }
    },
    /**
     * 坡度级别格式化
     */
    pdjbF() {
      const { abbreviation } = this.choosedLayer.data || {};
      let key, pdjb;
      if (abbreviation === "xztb") {
        key = "gdpdj";
      }
      if (abbreviation === "yjjbnt") {
        key = "gdpdjb";
      }
      if (key) {
        pdjb = this.choosedFreckle.data[key];
        this.$set(this.choosedFreckle.data, "pdjbF", pdjb);
      }
      if (!key || !pdjb) {
        return false;
      }
      try {
        getLayerTypeAttrFilter({ abbreviation }).then((resp) => {
          resp.data[abbreviation].forEach((item) => {
            if (
              item.filterType.toLowerCase() !== key ||
              String(item.filterAttrKey) !== String(pdjb).replace(/ /g, "")
            ) {
              return false;
            }
            let pdjbF;
            if (
              ["(", "（", "[", "【"].includes(
                item.filterAttrValue.substring(0, 1)
              )
            ) {
              pdjbF = pdjb + item.filterAttrValue;
            } else {
              pdjbF = pdjb + "(" + item.filterAttrValue + ")";
            }
            this.$set(this.choosedFreckle.data, "pdjbF", pdjbF);
          });
        });
      } catch (e) {
        console.log(e);
      }
    },
    /**
     * 接收地图传值
     * @param type 1:以地找视频 2:测距 3:测面 4:图层树选中 5:图斑选中 6: 清除图层 7: 打开标绘, 8: 清除电子围栏选中
     * @param layerId 选中图层id
     * @param layerData 选中图层数据
     * @param freckleId 选中图斑id
     * @param freckleData 选中图斑数据
     * @param vertexPercentList 顶点百分比坐标
     * @param vertexLngLats 顶点经纬度坐标
     * @param centerPointPtz { pan: 87, tilt: -86, zoom: 1.0 } 中心点PTZ
     */
    mapToVideo(params) {
      console.log("=================接收地图传值================", params);
      const {
        type,
        layerId,
        layerData,
        freckleId,
        freckleData,
        vertexPercentList,
        vertexLngLats,
        centerPointPtz,
      } = params;
      if ([1, 2, 3].includes(type)) {
        this.mapDrawing = type;
        this.clear(false);
        this.drawItem.points = vertexPercentList.map((item) => {
          return {
            percentX: item.percentU,
            percentY: item.percentV,
          };
        });
        this.drawFinish(undefined, type);
      }
      if (type === 1 && centerPointPtz) {
        this.doGotoPtz(centerPointPtz);
      }
      if (type === 4) {
        this.choosedLayer.id = layerId;
        this.choosedLayer.data = layerData;
        this.getElectronicFence();
      }
      if (type === 5) {
        this.clearChooseElectronicFence();
        if (freckleId) {
          this.queryByLayerIdAndFeatureIdFn(freckleId);
        }
      }
      if (type === 6) {
        this.mapDrawing = false;
        this.clear(false);
      }
      if (type === 7) {
        this.drawing = false;
        this.mapDrawing = false;
        this.clear();
      }
      if (type === 8) {
        this.clearChooseElectronicFence();
      }
    },
    /**
     * 传值给地图
     * @param type 1:以地找视频 2:测距 3:测面 4:清除标绘(1、2、3) 5:电子围栏选中, 6: 打开标绘, 99: 提示信息
     * @param layerId 选中图层id
     * @param freckleId 选中图斑id
     * @param vertexLngLatList [{ longitude: "111.111111", latitude: "33.333333" }] 顶点经纬度坐标
     */
    videoToMap(params) {
      console.log("=================传值给地图================", params);
      this.$emit("videoToMap", params);
    },
    /**
     * 初始化绘制画板
     */
    initCanvas() {
      const $drawBody = $("#" + this.pid);
      if (!$drawBody?.length) {
        return false;
      }
      const w = parseInt($drawBody.outerWidth());
      const h = parseInt($drawBody.outerHeight());
      if (w === this.size.w && h === this.size.h) {
        return false;
      }
      console.log("%c 画布重置==", "color: blue", w, h, this.size);
      if (this.drawCanvas) {
        this.drawCanvas.dispose();
        this.drawCanvas = false;
      }
      this.size.w = w;
      this.size.h = h;
      this.$nextTick(() => {
        this.loadCanvas();
      });
    },
    /**
     * 画板实例
     */
    loadCanvas() {
      this.drawCanvas = new fabric.Canvas("draw_canvas_" + this.pid, {
        fireRightClick: true, // 启用右键，button的数字为3
        stopContextMenu: true, // 禁止默认右键菜单
        hoverCursor: "pointer", //鼠标样式
        moveCursor: "default", //移动时鼠标样式
        selection: false, //分组选择是否可用
      });
      this.drawCanvas.on({
        "mouse:down": (e) => {
          //鼠标按下
          if (this.drawing) {
            if (e.button === 3) {
              //右键取消
              this.drawCancel(e);
            } else {
              this.drawClick(e);
            }
          }
          if (this.drawing && !this.drawItem.finish.length) {
            return false;
          }
          this.chooseElectronicFence(e);
        },
        "mouse:move": (e) => {
          //鼠标移动
          if (this.drawing) {
            this.drawMove(e);
          }
        },
        "mouse:dblclick": (e) => {
          //鼠标双击
          if (this.drawing) {
            this.drawFinish(e);
          }
        },
      });
      this.drawFinish();
      const electronicFences = this.electronicFenceDrawList.map((item) => {
        return item.data;
      });
      this.doElectronicFenceDraw(electronicFences, true);
    },
    /**
     * 标绘点击
     */
    drawClick(e) {
      if (this.drawItem.finish.length) {
        return false;
      }
      const { w, h } = this.size;
      const { stroke } = this.getColor();
      const { x, y } = e.pointer;
      const line = new fabric.Line([x, y, x, y], {
        selectable: false,
        strokeWidth: 3,
        stroke,
      });
      this.drawItem.points.push({
        percentX: x / w,
        percentY: y / h,
      });
      this.drawItem.lines.push(line);
      this.drawCanvas.add(line);
      if (this.drawing === 4) {
        this.countHeight(1);
      }
    },
    /**
     * 标绘移动
     */
    drawMove(e) {
      if (this.drawItem.finish.length) {
        return false;
      }
      const lastLine = this.drawItem.lines[this.drawItem.lines.length - 1];
      if (lastLine) {
        lastLine.set({
          x2: e.pointer.x,
          y2: e.pointer.y,
        });
        this.drawCanvas.renderAll();
      }
    },
    /**
     * 标绘取消
     */
    drawCancel(e) {
      if (!this.drawItem.lines.length || this.drawItem.finish.length) {
        return false;
      }
      this.drawCanvas.remove(
        this.drawItem.lines[this.drawItem.lines.length - 1]
      );
      this.drawItem.points.pop();
      this.drawItem.lines.pop();
      this.drawItem.lines[this.drawItem.lines.length - 1]?.set({
        x2: e.pointer.x,
        y2: e.pointer.y,
      });
      this.drawCanvas.renderAll();
      if (this.drawing === 4) {
        this.countHeight(-1);
      }
    },
    /**
     * 标绘双击
     */
    drawFinish(e, type = this.drawing || this.drawItem?.type) {
      if (!this.drawItem || (this.drawItem.finish.length && e)) {
        return false;
      }
      if (e) {
        this.drawCancel(e);
      }
      const { w, h } = this.size;
      const points = this.drawItem.points.map((item) => {
        return {
          x: item.percentX * w,
          y: item.percentY * h,
        };
      });
      if (points.length < 2 || ([1, 3].includes(type) && points.length < 3)) {
        !e && this.clear(false);
        return false;
      }
      this.clear(true);
      const { stroke, fill } = this.getColor(type);
      if ([1, 3].includes(type)) {
        const left = this.findLeftPaddingForRoof(points);
        const top = this.findTopPaddingForRoof(points);
        const polygon = new fabric.Polygon(points, {
          type: 1,
          selectable: false,
          strokeWidth: 3,
          fill,
          stroke,
          left,
          top,
        });
        this.drawCanvas.add(polygon);
        this.drawItem.finish.push(polygon);
        const center = polygon.getCenterPoint();
        this.drawItem.centerPoint = [
          {
            percentX: center.x / w,
            percentY: center.y / h,
          },
        ];
      }
      points.forEach((item, index) => {
        if ([2, 4].includes(type) && points[index + 1]) {
          //测距
          const line = new fabric.Line(
            [item.x, item.y, points[index + 1].x, points[index + 1].y],
            {
              type: 2,
              selectable: false,
              strokeWidth: 3,
              stroke,
            }
          );
          this.drawCanvas.add(line);
          this.drawItem.finish.push(line);
        }
        if ([2, 3, 4].includes(type)) {
          const circle = new fabric.Circle({
            type: 3,
            selectable: false,
            evented: true,
            left: item.x - 6,
            top: item.y - 6,
            strokeWidth: 3,
            radius: 6,
            fill: "#fff",
            stroke,
          });
          this.drawCanvas.add(circle);
          this.drawItem.finish.push(circle);
        }
      });
      this.drawItem.type = type;
      if (type !== 4) {
        //测高度
        this.toMapLngLat(e, type);
      }
    },
    /**
     * 测高度
     */
    countHeight(flag) {
      const points = this.drawItem.points;
      const heights = this.drawItem.heights;
      if (flag < 0) {
        heights.pop();
        return false;
      }
      if (!points.length) {
        return false;
      }
      const { percentX, percentY } = points[points.length - 1];
      const p = {
        percentX,
        percentY,
      };
      heights.push(p);
      const params = {
        deviceCode: this.deviceCode,
        channelCode: this.channelCode,
        ...p,
      };
      getCameraCoord(params).then((resp) => {
        if (resp.code === 200 && resp.data) {
          const coordtesxt = resp.data.coordtesxt;
          if (coordtesxt) {
            const text = coordtesxt.split(")").shift();
            const difheight = text.split(" ").pop();
            this.$set(p, "difheight", difheight);
          }
        } else {
          $message.e(resp.msg);
        }
      });
    },
    /**
     * 适配测距
     */
    fitHeight(index) {
      if (index === 0) {
        return "起点";
      }
      const heights = this.drawItem.heights;
      if (this.drawItem.finish.length && index === heights.length - 1) {
        let sum = 0;
        for (let i = heights.length - 1; i >= 0; i--) {
          if (i < 1) {
            break;
          }
          if (heights[i].difheight === undefined) {
            continue;
          }
          sum += Math.abs(heights[i].difheight - heights[i - 1].difheight);
        }
        if (sum) {
          sum = "共" + sum.toFixed(2) + "米";
        }
        return sum;
      }
      let h = Math.abs(heights[index].difheight - heights[index - 1].difheight);
      if (h) {
        h = h.toFixed(2) + "米";
      }
      return h;
    },
    /**
     * 百分比点转经纬度
     */
    toMapLngLat(e, type) {
      const params = {
        deviceCode: this.deviceCode,
        channelCode: this.channelCode,
        points: [...this.drawItem.points, ...this.drawItem.centerPoint],
      };
      turnLatitudeByPoint(params).then((resp) => {
        if (resp.code === 200) {
          let points = resp.data.points || [];
          let centerPoint;
          if (this.drawItem.centerPoint.length) {
            centerPoint = points.slice(points.length - 1, points.length);
            points = points.slice(0, points.length - 1);
          }
          if ([1, 3].includes(type) && points.length) {
            points.push(points[0]);
          }
          if (e) {
            this.videoToMap({
              type,
              vertexLngLatList: points,
            });
          }
          this.countDistanceAndArea(points, centerPoint, type);
        } else {
          $message.a(resp);
        }
      });
    },
    /**
     * 测距计算距离、面积
     */
    countDistanceAndArea(points, centerPoint, type) {
      const lngLats = points.map((item) => {
        return [item.longitude, item.latitude];
      });
      const distance = getLineStringLength(lngLats);
      this.drawItem.distances = distance.lengthArr.map((item) =>
        (item * 1000).toFixed(2)
      );
      this.drawItem.distanceSum = (distance.sum * 1000).toFixed(2);
      if (centerPoint) {
        const lngLats2 = [
          [this.lng, this.lat],
          [centerPoint[0].longitude, centerPoint[0].latitude],
        ];
        const distance2 = getLineStringLength(lngLats2);
        this.drawItem.toDeviceDistance = (distance2.sum * 1000).toFixed(2);
      }
      if ([1, 3].includes(type)) {
        const area = getPolygonArea(lngLats);
        this.drawItem.area = Number(area).toFixed(2);
      }
    },
    /**
     * 适配测距
     */
    fitDistance(index) {
      if (index === 0) {
        return "起点";
      }
      if (index === this.drawItem.points.length - 1) {
        return "共" + this.drawItem.distanceSum + "米";
      }
      return this.drawItem.distances[index - 1] + "米";
    },
    /**
     * 删除点 type 1: 测距, 2: 测高度
     */
    removePoint(index, type) {
      this.drawItem.points.splice(index, 1);
      if (type === 2) {
        this.drawItem.heights.splice(index, 1);
      }
      this.clear(true);
      this.drawFinish(true);
    },
    /**
     * 生成标绘颜色
     */
    getColor(type = this.drawing) {
      let stroke = "rgba(209, 185, 35, 0.8)",
        fill = "rgba(209, 185, 35, 0.3)";
      if ([0, 1, 2, 3, 4].includes(type)) {
        stroke = "rgba(237, 81, 88, 1)";
        fill = "rgba(237, 81, 88, 0.30)";
      }
      return { stroke, fill };
    },
    /**
     * 开启、关闭标绘, 1: 以视频找地, 2: 测距, 3: 测面积, 4: 测高度
     */
    doDrawing(type) {
      this.clear();
      if (this.drawing === type) {
        this.drawing = type = false;
      }
      this.drawing = false;
      this.mapDrawing = false;
      if (type) {
        if (this.doDrawingLoading) {
          return false;
        }
        const params = {
          deviceCode: this.deviceCode,
        };
        this.doDrawingLoading = true;
        pointsValid(params)
          .then((resp) => {
            this.doDrawingLoading = false;
            if (resp.code === 200) {
              this.doMessTs("点击开始绘制，右键撤销上一步绘制，双击结束绘制。");
              this.drawing = type;
              this.videoToMap({
                type: 6,
              });
            } else {
              this.doMessTs("该摄像机暂未标定，请联系铁塔管理员。");
            }
          })
          .catch(() => {
            this.doDrawingLoading = false;
            this.doMessTs("该摄像机暂未标定，请联系铁塔管理员。");
          });
      }
    },
    /**
     * 开启、关闭云台操控
     */
    cloudControlShow(cloudControlSwitch = !this.cloudControlSwitch) {
      this.$emit("cloudControl", cloudControlSwitch);
    },
    /**
     * 清除
     */
    clear(reload) {
      this.drawItem?.lines?.forEach((item) => {
        this.drawCanvas?.remove(item);
      });
      this.drawItem?.circles?.forEach((item) => {
        this.drawCanvas?.remove(item);
      });
      this.drawItem?.finish?.forEach((item) => {
        this.drawCanvas?.remove(item);
      });
      this.drawItem = {
        type: false,
        points: reload ? this.drawItem.points : [], //标绘点
        centerPoint: [], //中心点
        lines: [], //标绘线
        finish: [], //标绘完成
        distances: [], //距离
        distanceSum: "", //距离和
        heights: reload ? this.drawItem.heights : [], //高
        toDeviceDistance: "", //中心点距离摄像机距离
        area: "", //面积
      };
      if (reload === undefined) {
        this.videoToMap({
          type: 4,
        });
      }
    },
    /**
     * 查询预置位列表
     */
    getPresetList() {
      const params = {
        deviceCode: this.deviceCode, //设备编码
        channelCode: this.channelCode, //通道编码
        customerCode: this.tenantId, //租户id
      };
      queryPresetList(params).then((resp) => {
        if (resp.code === 200) {
          this.presetList = resp.data || [];
          this.doSort();
        }
      });
    },
    /**
     * 排序
     */
    doSort(key) {
      if (key) {
        if (this.sort.key !== key) {
          this.sort.key = key;
          this.sort.asc = true;
        } else {
          this.sort.asc = !this.sort.asc;
        }
      }
      if (!this.presetList) {
        return false;
      }
      this.presetList.sort((item1, item2) => {
        const text1 = String(item1[this.sort.key]);
        const text2 = String(item2[this.sort.key]);
        let b;
        if (this.sort.asc) {
          b = text1.localeCompare(text2, "zh");
        } else {
          b = text2.localeCompare(text1, "zh");
        }
        return b;
      });
    },
    /**
     * 选中预置位
     */
    choosePreset(item, choosed) {
      this.choosedPreset = +item.presetId;
      this.gotoPreset(item, !choosed);
      this.getElectronicFence(choosed);
    },
    /**
     * 查询电子围栏
     */
    getElectronicFence(choosed) {
      this.clearElectronicFence(!choosed);
      clearTimeout(this.getElectronicFenceTiming);
      this.getElectronicFenceTiming = setTimeout(() => {
        if (
          !this.choosedPreset ||
          !this.choosedLayer.id ||
          !this.electronicFenceSwitch
        ) {
          return false;
        }
        const params = {
          deviceCode: this.deviceCode, //设备编码
          channelCode: this.channelCode, //通道编码
          presetId: this.choosedPreset, //预置位 id
          layerId: this.choosedLayer.id, //图层id
        };
        queryPolygonPatchShow(params).then((resp) => {
          if (resp.code === 200 && resp.data.length) {
            this.doElectronicFenceDraw(resp.data, false, choosed);
          }
        });
      }, 50);
    },
    /**
     * 图斑id与电子围栏的关系
     */
    queryByLayerIdAndFeatureIdFn(freckleId) {
      if (!freckleId) {
        return false;
      }
      const params = {
        layerId: this.choosedLayer.id, // 图层ID
        featureId: freckleId, // 图斑ID
        deviceCode: this.deviceCode, //设备
        channelCode: this.channelCode, //通道
      };
      queryByLayerIdAndFeatureId(params)
        .then((resp) => {
          if (resp.code === 200 && resp.data) {
            if (
              this.deviceCode !== resp.data.deviceCode ||
              this.channelCode !== resp.data.channelCode
            ) {
              this.videoToMap({
                type: 99,
                text: "该图斑已在其他摄像机或者其他通道中绘制了电子围栏，请选择其他摄像机通道查看。",
              });
              return false;
            }
            this.mapToChoosePreset(resp.data);
          } else {
            this.videoToMap({
              type: 99,
              text: "该图斑暂未绘制电子围栏，请在“配置管理-图斑标绘”中绘制。",
            });
          }
        })
        .catch(() => {
          this.videoToMap({
            type: 99,
            text: "该图斑暂未绘制电子围栏，请在“配置管理-图斑标绘”中绘制。",
          });
        });
    },
    /**
     * 地图选中图斑切换预置位
     */
    mapToChoosePreset(polygon) {
      if (+polygon.presetId !== this.choosedPreset) {
        this.choosePreset({ presetId: polygon.presetId }, polygon);
        return false;
      }
      this.electronicFenceDrawList.forEach((item) => {
        if (item.data.polygonId === polygon.polygonId) {
          item.data.selected = false;
          this.chooseElectronicFence(
            {
              target: item,
            },
            false,
            false
          );
        }
      });
    },
    /**
     * 清除选中电子围栏
     */
    clearChooseElectronicFence(call) {
      const { stroke, fill } = this.getColor(false);
      this.electronicFenceDrawList.forEach((item) => {
        if (item.data.selected && call) {
          this.videoToMap({
            type: 5,
            layerId: "",
            freckleId: "",
          });
        }
        this.$set(item.data, "selected", false);
        item.set({
          stroke,
          fill,
        });
      });
      this.drawCanvas.renderAll();
      this.choosedFreckle = {
        id: "",
        data: "",
      };
    },
    /**
     * 选中电子围栏
     */
    chooseElectronicFence(e, back, call = true) {
      if (!e?.target?.data) {
        this.clearChooseElectronicFence(call);
        return false;
      }
      const data = JSON.parse(JSON.stringify(e.target.data));
      this.clearChooseElectronicFence(call);
      if (!data.selected || back) {
        const { stroke, fill } = this.getColor(0);
        e.target.set({
          stroke,
          fill,
        });
        this.drawCanvas.bringToFront(e.target);
        this.choosedFreckle.id = data.featureId;
        this.getXjxlFeaturesByIds(call, e.target.data);
      }
      this.drawCanvas.renderAll();
    },
    /**
     * 查询图斑信息
     */
    async getXjxlFeaturesByIds(call, data) {
      const params = {
        layerID: this.choosedLayer.id,
        tbbhs: [this.choosedFreckle.id],
        returnAllCols: true,
        returnGeometry: true,
      };
      let resp, geometry;
      try {
        resp = await getXjxlFeaturesByIds(params);
      } catch (e) {
        resp = {};
      }
      if (resp.code === 200 && resp.data?.geoData?.features?.length) {
        this.choosedFreckle.data =
          resp.data.geoData.features[0].properties || {};
        geometry = resp.data.geoData.features[0].geometry;
        this.pdjbF();
      }
      if (call) {
        this.videoToMap({
          type: 5,
          layerId: this.choosedLayer.id,
          freckleId: this.choosedFreckle.id,
          tbbh: data.tbbh,
          geometry,
        });
      }
      this.$set(data, "selected", true);
      this.fitFeatureDialog();
    },
    /**
     * 适配图斑信息弹窗
     */
    fitFeatureDialog() {
      this.$nextTick(() => {
        const $video = $("#" + this.pid).parents(".video-play");
        const videoStyle = $video[0].getBoundingClientRect();
        $video.find(".electronicFence-selected .info").each(function () {
          $(this).removeAttr("style");
          const itemStyle = $(this)[0].getBoundingClientRect();
          if (itemStyle.top < videoStyle.top) {
            $(this).css("top", itemStyle.height + 29 + "px");
          }
          if (itemStyle.left < videoStyle.left) {
            $(this).css("left", videoStyle.left - itemStyle.left + 3 + "px");
          }
          if (itemStyle.right > videoStyle.right) {
            $(this).css("left", videoStyle.right - itemStyle.right - 3 + "px");
          }
        });
      });
    },
    /**
     * 电子围栏标绘
     */
    doElectronicFenceDraw(list, back, choosed) {
      this.clearElectronicFence();
      if (!list?.length) {
        return false;
      }
      const { w, h } = this.size;
      list.forEach((item) => {
        const { stroke, fill } = this.getColor(false);
        const points = JSON.parse(item.polygonPoints || "[]").map((it) => {
          return {
            x: it.percentX * w,
            y: it.percentY * h,
          };
        });
        const left = this.findLeftPaddingForRoof(points);
        const top = this.findTopPaddingForRoof(points);
        const polygon = new fabric.Polygon(points, {
          type: 1,
          selectable: false,
          strokeWidth: 3,
          fill,
          stroke,
          left,
          top,
          data: item,
        });
        this.drawCanvas.add(polygon);
        const center = polygon.getCenterPoint();
        item.center = {
          percentX: center.x / w,
          percentY: center.y / h,
        };
        this.electronicFenceDrawList.push(polygon);
        if (choosed?.polygonId === item.polygonId || (back && item.selected)) {
          this.chooseElectronicFence(
            {
              target: polygon,
            },
            back,
            false
          );
        }
      });
      this.fitFeature();
    },
    /**
     * 适配图斑编号小红旗
     */
    fitFeature() {
      const $video = $("#" + this.pid);
      $video.find(".electronicFence-selected").each(function () {
        $(this).removeClass("left");
      });
      this.$nextTick(() => {
        const videoStyle = $video[0].getBoundingClientRect();
        $video.find(".electronicFence-selected").each(function () {
          const itemStyle = $(this).find(".feature")[0].getBoundingClientRect();
          if (itemStyle.right > videoStyle.right) {
            $(this).addClass("left");
            $(this)[0].style.setProperty(
              "--dialogX",
              -itemStyle.width / 2 + "px"
            );
          } else {
            $(this).removeClass("left");
            $(this)[0].style.setProperty(
              "--dialogX",
              itemStyle.width / 2 + "px"
            );
          }
        });
      });
    },
    /**
     * 清除电子围栏标绘
     */
    clearElectronicFence(call) {
      this.electronicFenceDrawList.forEach((item) => {
        this.drawCanvas?.remove(item);
      });
      this.electronicFenceDrawList = [];
      if (call) {
        this.videoToMap({
          type: 5,
          layerId: "",
          freckleId: "",
        });
      }
    },
    /**
     * 打开预置位
     */
    doPresetShow() {
      this.presetShow = !this.presetShow;
    },
    /**
     * 转动到预置位
     */
    gotoPreset(item, ts = true) {
      if (!this.checkPermissions("video:videoPreset", ts)) {
        return false;
      }
      let params = {
        deviceCode: this.deviceCode, //设备编码
        channelCode: this.channelCode, //通道编码
        presetId: item.presetId, //预置位 id
        customerCode: this.tenantId, //租户id
      };
      doControlPreset(params).then((resp) => {
        if (resp.code === 200) {
          ts && $message.s("转动预置位成功");
        } else {
          ts && $message.a(resp);
        }
      });
    },
    /**
     * 转动到ptz
     */
    doGotoPtz(center, ts) {
      const params = {
        deviceCode: this.deviceCode, //设备编码
        channelCode: this.channelCode, //通道编码
        pan: center.pan,
        tilt: center.tilt,
        zoom: center.zoom,
      };
      doPtzLocationCmd(params).then((resp) => {
        if (resp.code !== 200) {
          ts && $message.a(resp);
          return false;
        }
        ts && $message.s("转动到ptz操作成功");
      });
    },
    /**
     * 限制最小大小
     */
    limitMinSize() {
      this.resizeCount = this.resizeCount || 1;
      const winW = $(window).width();
      const winH = $(window).height();
      const $parent = $("#" + this.pid).parents(".video-dialog");
      const pW = Math.ceil($parent.outerWidth() * this.myTransform);
      const pH = Math.ceil($parent.outerHeight() * this.myTransform);
      if (this.unCenter) {
        this.setPosition(this.minWidth, this.minHeight);
      } else {
        if (pW < this.minWidth) {
          $parent.css({
            width: this.minWidth,
          });
          if (this.resizeCount === 1) {
            $parent.css({
              left: (winW - this.minWidth) / 2,
            });
          }
        }
        if (pH < this.minHeight) {
          $parent.css({
            height: this.minHeight,
          });
          if (this.resizeCount === 1) {
            $parent.css({
              top: (winH - this.minHeight) / 2,
            });
          }
        }
      }
      this.resizeCount++;
    },
    setPosition(zoomW = this.minWidth, zoomH = this.minHeight) {
      let bottom,
        right,
        { top, left, bTop, rLeft, realW, realH } = this.moveVideoBoxPosition();
      if (rLeft - left > zoomW) {
        zoomW = realW;
        zoomH = realH;
      }
      const winW = $(window).width();
      const winH = $(window).height();
      if (top + zoomH > winH && top > winH / 2) {
        top = bTop - zoomH;
      }
      if (left + zoomW > winW && left > winW / 2) {
        left = rLeft - zoomW;
      }
      bottom = winH - top - zoomH;
      right = winW - left - zoomW;
      if (top < 20) {
        top = 20;
      }
      if (left < 20) {
        left = 20;
      }
      if (bottom - 20 < 0) {
        top += bottom - 20;
      }
      if (right - 20 < 0) {
        left += right - 20;
      }
      let params;
      if (this.division === "1") {
        params = {
          width: zoomW / this.myTransform,
          height: zoomH / this.myTransform,
          position: "fixed",
          left: 0,
          top: 0,
        };
      } else {
        params = {
          width: zoomW / this.myTransform,
          height: zoomH / this.myTransform,
          left,
          top,
        };
      }
      $("#" + this.pid)
        .parents(".video-dialog")
        .css(params);
    },
    /**
     * 组件移动记录位置
     */
    moveVideoBoxPosition() {
      const $parent = $("#" + this.pid).parents(".video-dialog");
      const top = $parent.offset().top;
      const left = $parent.offset().left;
      const realW = Math.ceil($parent.outerWidth() * this.myTransform);
      const realH = Math.ceil($parent.outerHeight() * this.myTransform);
      const bTop = top + realH;
      const rLeft = left + realW;
      //保存移动后的位置
      this.leftPosition = left;
      this.topPosition = top;
      if (this.leftPosition < 20) {
        this.leftPosition = 20;
      }
      if (this.topPosition < 20) {
        this.topPosition = 20;
      }
      return { top, left, bTop, rLeft, realW, realH };
    },
    /**
     * 初始化计算宽高
     */
    initViewBox() {
      const winW = $(window).width();
      if (winW < 1920) {
        return false;
      }
      let zoomW = (this.minWidth * winW) / 1920;
      if (zoomW > this.minWidth) {
        this.minWidth = zoomW;
      }
      this.minHeight = (this.minWidth * 9) / 16;
    },
    /**
     * 放大缩小
     */
    doZoom() {
      this.$emit("update:isZoomIn", !this.isZoomIn);
    },
    /**
     * 退出
     */
    doOut() {
      this.$emit("out");
    },
    doMessTs(text) {
      this.$refs["ts"].doMessTs(text, true);
    },
    /**
     * 校验权限
     */
    checkPermissions(value, ts) {
      const all = "*:*:*";
      const permissions = this.permissions || [];
      const b = permissions.includes(all) || permissions.includes(value);
      if (!b && ts) {
        if (value === "video:videoControlRole") {
          $message.w("您没有云台控制权限，请联系管理员。");
        } else if (value === "video:videoPreset") {
          $message.w("您没有预置位控制权限，请联系管理员。");
        } else if (value === "video:videoCruisePlan") {
          $message.w("您没有巡航控制权限，请联系管理员。");
        } else {
          $message.w("您暂无权限，如需获取权限，请联系管理员。");
        }
      }
      return b;
    },
    /**
     * 生成uuid
     */
    getUUID() {
      const crypto = window.crypto || window.msCrypto;
      const array = new Uint32Array(1);
      const r = crypto.getRandomValues(array)[0];
      return Date.now() + "2" + r;
    },
    /**
     * 精准计算top
     */
    findTopPaddingForRoof(roofPoints) {
      let result = 999999;
      roofPoints.forEach((i) => {
        if (i.y < result) {
          result = i.y;
        }
      });
      return result;
    },
    /**
     * 精准计算left
     */
    findLeftPaddingForRoof(roofPoints) {
      let result = 999999;
      roofPoints.forEach((i) => {
        if (i.x < result) {
          result = i.x;
        }
      });
      return result;
    },
  },
  beforeDestroy() {
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
    clearTimeout(this.resizeTiming);
    clearTimeout(this.getElectronicFenceTiming);
    clearTimeout(this.getPtzTiming);
  },
};
</script>
<style scoped lang="scss">
.video-to-map {
  width: 38%;
  height: 38%;
  position: absolute;
  bottom: 0;
  right: 0;
  z-index: 9;
  &.bigScreen {
    width: 50%;
    height: 50%;
  }
  .preset {
    width: 200px;
    height: 32px;
    text-align: center;
    line-height: 30px;
    border: 1px solid #fff;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    position: absolute;
    left: 12px;
    top: 12px;
    cursor: pointer;
    z-index: 200;
    .jt {
      position: absolute;
      right: 10px;
    }
    .preset-list {
      display: none;
      width: 100%;
      position: absolute;
      left: 0;
      top: calc(100% + 6px);
      background: rgba(0, 0, 0, 0.7);
      text-align: left;
      max-height: 256px;
      overflow: auto;
      border-radius: 4px;
      .item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 0 12px;
        &.on,
        &:hover {
          background: rgba(255, 255, 255, 0.2);
        }
        span {
          flex: 1;
          margin-right: 10px;
          max-width: 10em;
          overflow: hidden;
          word-break: keep-all;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
        i {
          display: none;
          font-size: 18px;
        }
        &.on {
          i {
            display: block;
          }
        }
      }
      .c-no-datas-block {
        margin: 8px auto;
        &:before {
          background-image: url("~@/assets/image/noData-normal.png");
        }
      }
    }
    &.on {
      border-radius: 4px 4px 0 0;
      .jt {
        transform: rotate(180deg);
      }
      .preset-list {
        display: block;
      }
    }
  }
  .v-btns {
    display: flex;
    position: absolute;
    left: 224px;
    top: 12px;
    z-index: 12;
    .btn {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 32px;
      min-width: 32px;
      background: rgba(0, 0, 0, 0.7);
      color: #fff;
      border-radius: 8px;
      margin-left: 6px;
      position: relative;
      &:first-child {
        margin-left: 0;
      }
      .el-switch {
        width: 32px;
        height: 16px;
        margin: 0 12px;
        /deep/ .el-switch__core {
          width: 100% !important;
          height: 16px !important;
          background: none;
          border: 1px solid #fff;
          border-radius: 16px;
          &:after {
            width: 12px;
            height: 12px;
            background: #fff;
          }
        }
        &.is-checked {
          /deep/ .el-switch__core {
            background: #909399 !important;
            border: 1px solid #909399 !important;
            &:after {
              margin-left: -13px;
            }
          }
        }
      }
      i {
        line-height: 1;
        font-size: 20px;
      }
    }
    .goto-ptz,
    .ptz-info {
      position: absolute;
      left: 0;
      top: calc(100% + 6px);
      background: rgba(0, 0, 0, 0.7);
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      height: 32px;
      color: #fff;
      font-size: 14px;
      cursor: default;
      text-align: center;
    }
    .goto-ptz {
      padding: 3px;
      line-height: 26px;
      input {
        width: 44px;
        height: 100%;
        border: 0;
        outline: none;
        background: rgba(255, 255, 255, 0.2);
        border-radius: 4px;
        margin-right: 3px;
        color: #fff;
        padding: 0 6px;
        font-size: 14px;
        &::placeholder {
          color: #fff;
        }
      }
      span {
        display: block;
        width: 44px;
        height: 100%;
        color: #fff;
        background: rgba(255, 255, 255, 0.4);
        border-radius: 4px;
        cursor: pointer;
        &:hover {
          opacity: 0.6;
        }
        &:active {
          opacity: 0.8;
        }
      }
    }
    .ptz-info {
      padding: 3px 12px;
      line-height: 26px;
      span {
        margin-left: 12px;
        white-space: nowrap;
        &:first-child {
          margin-left: 0;
        }
      }
    }
  }
  .r-btns {
    display: flex;
    flex-direction: column;
    position: absolute;
    right: 8px;
    bottom: 8px;
    z-index: 12;
    .btn {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 24px;
      width: 24px;
      background: #ffffff;
      color: #000;
      border-radius: 4px;
      box-shadow: 0px 2px 5px 0px rgba(168, 175, 184, 0.54);
      margin-top: 4px;
      i {
        line-height: 1;
        font-weight: bold;
        font-size: 18px;
      }
    }
  }
  .draw-canvas {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    z-index: 8;
    pointer-events: none;
  }
  &.electronicFence {
    .draw-canvas {
      pointer-events: auto;
    }
  }
  &.drawing {
    .draw-canvas {
      pointer-events: auto;
    }
    &:not(.end) {
      z-index: 99;
      .draw-canvas {
        z-index: 10;
      }
      .preset {
        z-index: 9;
      }
    }
  }
  .find-video-info {
    background: rgba(0, 0, 0, 0.7);
    border-radius: 4px;
    position: absolute;
    left: 12px;
    bottom: 12px;
    z-index: 100;
    pointer-events: none;
    padding: 12px;
    .item {
      display: flex;
      line-height: 1;
      font-size: 12px;
      margin-top: 6px;
      &:first-child {
        margin-top: 0;
      }
      .ll {
        width: 7em;
        text-align: right;
        color: rgba(255, 255, 255, 0.7);
      }
      .num {
        color: #fff;
      }
    }
  }
  .draw-area,
  .draw-distance {
    display: flex;
    align-items: center;
    height: 24px;
    line-height: 1;
    padding: 0 12px;
    background: rgba(0, 0, 0, 1);
    color: #ffffff;
    position: absolute;
    transform: translate(-50%, calc(-100% - 6px));
    word-break: keep-all;
    white-space: nowrap;
    border-radius: 4px;
    z-index: 100;
    font-size: 14px;
    i {
      text-align: center;
      margin-left: 6px;
      cursor: pointer;
      font-size: 19px;
      line-height: 1;
      &:first-child {
        margin-left: 0;
      }
    }
  }
  .draw-distance {
    transform: translate(calc(-50% + 1px), calc(-100% - 12px));
    &:after {
      content: "";
      width: 0;
      height: 0;
      border: 5px solid rgba(0, 0, 0, 0);
      position: absolute;
      left: 50%;
      top: 100%;
      transform: translateX(-50%);
      border-top-color: rgba(0, 0, 0, 1);
    }
  }
  .electronicFence-selected {
    position: absolute;
    z-index: 100;
    font-size: 14px;
    &.before {
      z-index: 201;
    }
    .info {
      width: 230px;
      background: rgba(0, 0, 0, 0.9);
      border-radius: 8px;
      position: absolute;
      left: 0;
      top: -3px;
      z-index: 2;
      transform: translate(
        calc(#{var(--dialogX, 0)} - 50%),
        calc(-100% - 45px)
      );
      .til {
        width: 100%;
        height: 33px;
        line-height: 32px;
        text-align: center;
        border-bottom: 1px solid;
        border-image: linear-gradient(
            90deg,
            rgba(255, 255, 255, 0),
            rgba(255, 255, 255, 0.35),
            rgba(255, 255, 255, 0)
          )
          1 1;
        color: #ffffff;
        text-shadow: 0px 1px 4px #ffffff;
        font-weight: 500;
        padding: 0 6px;
        word-break: keep-all;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .con {
        padding: 8px 6px;
        .item {
          display: flex;
          line-height: 26px;
          word-break: keep-all;
          white-space: nowrap;
          color: rgba(255, 255, 255, 0.7);
          span {
            flex: 1;
            color: #fff;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }
      }
    }
    .feature {
      display: block;
      min-width: 58px;
      text-align: center;
      height: 26px;
      line-height: 26px;
      border-radius: 13px;
      color: #fff;
      padding: 0 8px;
      font-size: 16px;
      position: absolute;
      left: 0;
      bottom: 0;
      background: linear-gradient(121deg, #e91d18 0%, #ba261f 100%);
      box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
      word-break: keep-all;
      white-space: nowrap;
      transform: translate(2px, -19px);
      &:before {
        content: "";
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background: #e41e19;
        position: absolute;
        left: 0;
        top: 50%;
        transform: translate(-6px, -50%);
      }
      &:after {
        content: "";
        width: 4px;
        height: 32px;
        background: #e41e19;
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateX(-4px);
      }
    }
    &.left {
      .feature {
        background: linear-gradient(121deg, #ba261f 0%, #e91d18 100%);
        transform: translate(calc(-100% - 2px), -19px);
        &:before {
          left: 100%;
          transform: translate(-4px, -50%);
        }
        &:after {
          left: 100%;
          transform: translateX(0);
        }
      }
    }
  }
  .ts {
    margin-top: 44px;
  }
  ::-webkit-scrollbar-thumb {
    background-color: rgba(255, 255, 255, 0.4) !important;
    &:hover {
      background-color: rgba(255, 255, 255, 0.55) !important;
    }
  }
  .el-scrollbar__bar {
    .el-scrollbar__thumb {
      background-color: rgba(255, 255, 255, 0.4) !important;
      &:hover {
        background-color: rgba(255, 255, 255, 0.55) !important;
      }
    }
  }
}
</style>
